package tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoSever {
    private ServerSocket serverSocket = null;

    public TcpEchoSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        //线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        //每次处理一个连接,每个连接可请求多次
        while (true) {
            //接收客户端的连接
            Socket clientSocket = serverSocket.accept();
            //每次accept一个客户端就创建一个线程，由线程处理客户端的需求
            /*Thread t = new Thread(() -> {
                processConnection(clientSocket);
            });
            t.start();*/
            threadPool.submit(() -> {
                processConnection1(clientSocket);
            });
        }
    }

    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线\n", clientSocket.getInetAddress().toString(),
        clientSocket.getPort());
        //使用Scanner和PrintWriter分别作为输入流和输出流
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            //从网络流中输入数据
            Scanner scNet = new Scanner(inputStream);
            //将数据输出到网络流中
            PrintWriter writer = new PrintWriter(outputStream);
            while (true) {
                //1.接收数据
                //连接断开(当客户端关闭时)
                //当客户端关闭其输出流（即OutputStream）时，
                // 服务器端的Scanner对象scNet会检测到输入流的结束，
                // 因此scNet.hasNext()将返回false，这导致服务器端退出循环，并打印出客户端下线的日志。
                if (!scNet.hasNext()) {
                    //打印日志
                    System.out.printf("[%s:%d] 客户端下线\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                String request = scNet.next();
                //2.处理分析数据
                String response = process(request);
                //3.返回响应到客户端
                writer.println(response);
                //刷新缓冲区
                writer.flush();
                //打印日志
                System.out.printf("[%s:%d] req: %s  rep: %s\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * processConnection1(Socket clientSocket)
     * 使用InputStream的read()方法直接从字节流中读取数据，这种方式是面向字节的，可以处理任何类型的数据，包括非文本数据。
     * 需要手动处理读取到的数据长度，使用inputStream.read(data)返回的字节数来确定实际读取的数据。
     * @param clientSocket
     */
    private void processConnection1(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            byte[] data = new byte[4096];
            while (true) {
                try {
                    int bytesRead = inputStream.read(data);
                    String request = new String(data, 0, bytesRead, StandardCharsets.UTF_8);
                    // 处理分析数据
                    String response = process(request);
                    // 返回响应到客户端
                    outputStream.write(response.getBytes(StandardCharsets.UTF_8));
                    outputStream.flush(); // 确保数据被发送
                    // 打印日志
                    System.out.printf("[%s:%d] req: %s rep: %s\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort(), request, response);
                } catch (SocketException e) {
                    // 客户端连接断开(当客户端关闭时)
                    System.out.printf("[%s:%d] 客户端下线\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoSever tcpEchoSever = new TcpEchoSever(9090);
        tcpEchoSever.start();
    }
}
